from pathlib import Path
from urllib.parse import urlparse, ParseResult

import m3u8
import wx
import re
import os



def get_parent_path(s):
    """获取url的父级path"""
    s = s.rstrip('/')  # 移除末尾斜杠
    parts = s.split('/')
    return '/'.join(parts[:-1]) if len(parts) > 1 else s

class UrlTrie:
    """用于获取url相同的部分"""
    common_prefix_url:str = ''
    def __init__(self):
        self.common_prefix_url=''

    def compare(self, _url):
        if self.common_prefix_url == '':
            self.common_prefix_url = _url
        else:
            if _url.startswith(self.common_prefix_url):
                pass
            else:
                self.common_prefix_url=get_parent_path(self.common_prefix_url)
                self.compare(_url)


class URLPath:
    def __init__(self, url):
        self.url = url

    @property
    def parent(self):
        parts=self.url.rsplit('/', 1)
        return URLPath(parts[0])

    @property
    def name(self):
        parts=self.url.rsplit('/', 1)
        return parts[1]

    def __str__(self):
        return self.url

class M3U8ConverterFrame(wx.Frame):
    """
    UI设计方案
    1. 主窗口布局
    ‌标题栏‌：显示工具名称，例如“M3U8转换工具”。
    ‌菜单栏‌：包括文件操作（打开、保存）、帮助等选项。
    ‌内容区域‌：
    ‌文件选择区域‌：一个按钮用于选择m3u8文件。
    ‌文件内容显示区域‌：一个文本框或列表框用于显示加载的m3u8文件内容。
    ‌修复按钮‌：一个按钮用于修复URL中不是ts结尾的链接。
    ‌批量路径修改区域‌：包括两个文本框，一个用于输入原URL路径，一个用于输入新URL路径，以及一个按钮用于执行批量修改。
    ‌状态栏‌：显示操作状态或提示信息。
    2. 功能描述
    ‌选择m3u8文件‌：点击文件选择按钮，弹出文件选择对话框，选择m3u8文件后，加载文件内容到显示区域。
    ‌加载m3u8文件内容‌：在选择文件后，自动读取文件内容并显示在文本框或列表框中。
    ‌修复URL‌：点击修复按钮，遍历文件内容中的URL，将不是以ts结尾的URL修改为以ts结尾（或者根据实际需求进行其他修复操作）。
    ‌批量路径修改‌：在批量路径修改区域输入原URL路径和新URL路径，点击按钮后，遍历文件内容，将所有匹配的原URL路径替换为新URL路径。
    """
    path_suffix:str = None

    def __init__(self):
        super().__init__(None, title="M3U8转换工具", size=(800, 600))
        self.file_path = ""
        self.m3u8_obj = None

        # 初始化UI
        self.init_ui()
        self.Centre()
        self.Show()

    def init_ui(self):
        panel = wx.Panel(self)

        # 主垂直布局
        main_sizer = wx.BoxSizer(wx.VERTICAL)

        # 文件选择区域（水平布局）
        file_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.btn_open = wx.Button(panel, label="选择M3U8文件")
        self.btn_open.Bind(wx.EVT_BUTTON, self.on_open_file)
        file_sizer.Add(self.btn_open, 0, wx.ALL, 5)

        self.file_path_label = wx.StaticText(panel, label="未选择文件")
        file_sizer.Add(self.file_path_label, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
        main_sizer.Add(file_sizer, 0, wx.EXPAND | wx.ALL, 5)

        # 文本显示区域（带滚动条）
        self.text_display = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
        main_sizer.Add(self.text_display, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)

        # 功能按钮区域（水平布局）
        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 左侧功能区域（垂直布局）
        left_btn_sizer = wx.BoxSizer(wx.VERTICAL)

        btn_sizer_4 = wx.BoxSizer(wx.HORIZONTAL)

        # 新增按钮：分析url可替换公用链接
        self.btn_repair = wx.Button(panel, label="分析url可替换公用链接")
        self.btn_repair.Bind(wx.EVT_BUTTON, self.analysis_common_url_path)
        btn_sizer_4.Add(self.btn_repair, 0, wx.BOTTOM, 5)

        # 修复按钮
        self.btn_repair = wx.Button(panel, label="修复TS链接")
        self.btn_repair.Bind(wx.EVT_BUTTON, self.on_repair_urls)
        btn_sizer_4.Add(self.btn_repair, 0, wx.BOTTOM, 5)

        left_btn_sizer.Add(btn_sizer_4, 0, wx.BOTTOM, 5)

        # 路径替换区域（网格布局）
        replace_sizer = wx.GridBagSizer(5, 5)
        replace_sizer.Add(wx.StaticText(panel, label="原路径:"), (0, 0), flag=wx.ALIGN_CENTER_VERTICAL )
        self.old_path = wx.TextCtrl(panel,style=wx.TE_READONLY)
        replace_sizer.Add(self.old_path, (0, 1), flag=wx.EXPAND)

        replace_sizer.Add(wx.StaticText(panel, label="新路径:"), (1, 0), flag=wx.ALIGN_CENTER_VERTICAL)
        self.new_path = wx.TextCtrl(panel)
        replace_sizer.Add(self.new_path, (1, 1), flag=wx.EXPAND)

        self.btn_replace = wx.Button(panel, label="执行替换")
        self.btn_replace.Bind(wx.EVT_BUTTON, self.on_replace_paths)
        replace_sizer.Add(self.btn_replace, (2, 0), (1, 2), flag=wx.EXPAND)

        replace_sizer.AddGrowableCol(1)  # 让第二列可扩展
        left_btn_sizer.Add(replace_sizer, 0, wx.EXPAND | wx.TOP, 5)

        btn_sizer.Add(left_btn_sizer, 1, wx.EXPAND | wx.ALL, 5)

        # 右侧保存按钮
        self.btn_save = wx.Button(panel, label="保存文件")
        self.btn_save.Bind(wx.EVT_BUTTON, self.on_save_file)
        btn_sizer.Add(self.btn_save, 0, wx.ALIGN_TOP | wx.ALL, 5)

        main_sizer.Add(btn_sizer, 0, wx.EXPAND | wx.ALL, 5)

        # 状态栏
        self.CreateStatusBar()

        panel.SetSizer(main_sizer)
        panel.Layout()

    def on_open_file(self, event):
        with wx.FileDialog(self, "选择M3U8文件",
                           wildcard="M3U8 files (*.m3u8)|*.m3u8",
                           style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as dlg:
            if dlg.ShowModal() == wx.ID_CANCEL:
                return
            self.file_path = dlg.GetPath()
            self.file_path_label.SetLabel(os.path.basename(self.file_path))
            self.file_name = os.path.basename(self.file_path)

            with open(self.file_path, 'r', encoding='utf-8') as f:
                self.m3u8_obj = m3u8.loads(f.read())

            self.text_display.SetValue(self.m3u8_obj.dumps())
            self.SetStatusText(f"已加载文件: {self.file_path}")
            self.clean_cache()



    def analysis_common_url_path(self,event):
        # print(self.file_path)
        # 获取文件所在目录
        dir_path=os.path.dirname(self.file_path)
        url_common_prefix=self.get_url_common_prefix(self.m3u8_obj.segments)
        _path=URLPath(url_common_prefix)
        print(f'url_common_prefix={url_common_prefix}')
        print(f'parentPath={_path.parent}')
        print(f'name={_path.name}')
        b=True
        path_suffix=None
        while b:
            if path_suffix:
                path_suffix = f'{path_suffix}{os.sep}{_path.name}'
            else :
                path_suffix = _path.name
            _path = _path.parent
            print(f'parentPath={_path.parent},pathName={_path.name}')
            b=not os.path.exists(f'{dir_path}{os.sep}{path_suffix}')
        self.path_suffix = path_suffix
        self.dir_path=dir_path
        self.url_link=str(_path)
        print(f'url {_path} 对应目录为 {dir_path}')
        # print(f'{dir_path}{os.sep}{path_suffix}')
        self.old_path.SetValue(f'{_path}/{path_suffix}')
        self.new_path.SetValue(f'{_path}/{path_suffix}')

        #打印 ffmpeg 转换mp4的命令行
        str1=self.file_name
        if str1.endswith('.m3u8'):
            str1=str1[:-5]

        print(
            """
        -i
        表示输入文件，后面跟的就是M3U8链接地址。
        -c
        copy
        表示直接复制流而不重新编码，能加快处理速度并保持原始视频的质量。
        -bsf: a
        aac_adtstoasc
        用于修复音频流，以确保音频正确转换并与MP4容器兼容。
        output.mp4
        是输出文件的名称，可以将其更改成你喜欢的文件名。
        """
        )
        print(f'ffmpeg -i {_path}/{self.file_name} -c copy  -bsf:a aac_adtstoasc {str1}.mp4')
        print(f'ffmpeg -i {_path}/{self.file_name} -c copy  {str1}.mp4')



        pass
    def get_url_common_prefix(self, segments):
        """
        获取url中相同的部分
        """
        trie = UrlTrie()
        for segment in segments:
            trie.compare(segment.uri)
        return trie.common_prefix_url

    def on_repair_urls(self, event):
        if not self.m3u8_obj:
            wx.MessageBox("请先打开文件！", "错误", wx.OK | wx.ICON_ERROR)
            return
        if not self.path_suffix:
            wx.MessageBox("请先分析url可替换公用链接！", "错误", wx.OK | wx.ICON_ERROR)
            return

        # 弹出确认对话框
        confirm_dialog = wx.MessageDialog(self, "确认要修复TS链接吗？", "确认", wx.YES_NO | wx.ICON_QUESTION)
        if confirm_dialog.ShowModal() != wx.ID_YES:
            return

        modified = False

        for segment in self.m3u8_obj.segments:
            if not segment.uri.endswith('.ts'):
                modified = True

        if not modified:
            self.SetStatusText('无需修复,符合规则')
            return

        import uuid
        newName = uuid.uuid4().hex
        m3u8Path=f'{newName}_m3u8_contents'
        newPath = f'{self.dir_path}{os.sep}{m3u8Path}'
        oldPath = f'{self.dir_path}{os.sep}{self.path_suffix}'
        os.makedirs(newPath)

        for segment in self.m3u8_obj.segments:
            if not segment.uri.endswith('.ts'):
                old_url=str(segment.uri)
                #url转换为本地绝对路径
                old_file_path = old_url.replace(self.url_link,self.dir_path)
                _url_path=URLPath(segment.uri)
                #新url
                segment.uri = f"{_url_path.parent.parent}/{m3u8Path}/{_url_path.name}.ts"
                import shutil
                # 复制文件
                shutil.copy(old_file_path, f'{newPath}{os.sep}{_url_path.name}.ts')

        self.text_display.SetValue(self.m3u8_obj.dumps())
        # 保存.m3u8文件
        save_path=f'{self.dir_path}{os.sep}{newName}.m3u8'
        with open(save_path, 'w', encoding='utf-8') as f:
            f.write(self.m3u8_obj.dumps())

        msg=f"TS链接已修复,文件已保存至: {save_path}"
        print(msg)
        self.SetStatusText(msg)

    def clean_cache(self):
        self.path_suffix=None
        self.old_path.SetValue(f'')
        self.new_path.SetValue(f'')

    def on_replace_paths(self, event):
        old = self.old_path.GetValue()
        new = self.new_path.GetValue()

        if not self.path_suffix:
            wx.MessageBox("请先分析url可替换公用链接！", "错误", wx.OK | wx.ICON_ERROR)

        if not old or not new:
            wx.MessageBox("请输入完整替换路径！", "错误", wx.OK | wx.ICON_ERROR)
            return

        modified = False

        for segment in self.m3u8_obj.segments:
            if old in segment.uri:
                segment.uri = segment.uri.replace(old, new)
                modified = True

        if modified:
            self.text_display.SetValue(self.m3u8_obj.dumps())
            self.SetStatusText(f"路径替换完成：{old} → {new}")
        else:
            self.SetStatusText("未找到匹配的替换内容")

    def on_save_file(self, event):
        if not self.m3u8_obj:
            return

        with wx.FileDialog(self, "保存文件",
                           wildcard="M3U8 files (*.m3u8)|*.m3u8",
                           style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as dlg:
            if dlg.ShowModal() == wx.ID_CANCEL:
                return

            save_path = dlg.GetPath()
            with open(save_path, 'w', encoding='utf-8') as f:
                f.write(self.m3u8_obj.dumps())

            self.SetStatusText(f"文件已保存至: {save_path}")

if __name__ == "__main__":
    app = wx.App()
    frame = M3U8ConverterFrame()
    app.MainLoop()
